#include "cem/mesh_manager.h"

#include <libmesh/boundary_info.h>
#include <libmesh/elem.h>
#include <libmesh/gmsh_io.h>
#include <libmesh/mesh_base.h>

#include <set>
#include <vector>

namespace cem
{

// 构造函数：基于通信器创建空网格对象。
MeshManager::MeshManager(const libMesh::Parallel::Communicator & comm)
    : _mesh(std::make_unique<libMesh::Mesh>(comm))
{
}

// 函数: prepare_empty_mesh
// 作用: 对空网格执行最小化准备流程，作为工程脚手架的冒烟验证。
void
MeshManager::prepare_empty_mesh()
{
    _mesh->allow_renumbering(true);
    _mesh->prepare_for_use();
}

// 函数: load_gmsh_mesh
// 作用: 使用 libMesh::GmshIO 读取 Gmsh 网格文件，并执行 prepare_for_use
// 完成基本预处理。 参数: path - 网格文件路径 返回: 无（若读取失败将由 libMesh
// 抛出异常）
void
MeshManager::load_gmsh_mesh(const std::string & path)
{
    _mesh->allow_renumbering(true);
    libMesh::GmshIO gmsh_io(*_mesh);
    gmsh_io.read(path);
    _mesh->prepare_for_use();

    // 补打默认边界标签：为所有外侧且尚未打标签的边界侧面设置默认 id=1
    // 这样在用户的 Gmsh 网格未提供边界物理组时，仍可施加 Dirichlet 以避免矩阵奇异
    libMesh::BoundaryInfo & boundary_info = _mesh->get_boundary_info();
    const libMesh::boundary_id_type default_id = 1;

    for (auto it = _mesh->active_local_elements_begin(); it != _mesh->active_local_elements_end();
         ++it)
    {
        const libMesh::Elem * elem = *it;
        const unsigned int side_count = elem->n_sides();
        for (unsigned int s = 0; s < side_count; ++s)
        {
            if (!elem->neighbor_ptr(s))
            {
                std::vector<libMesh::boundary_id_type> side_ids;
                boundary_info.boundary_ids(elem, s, side_ids);
                if (side_ids.empty())
                {
                    boundary_info.add_side(elem, s, default_id);
                }
            }
        }
    }
}

// 函数: get_mesh（可变）
libMesh::Mesh &
MeshManager::get_mesh()
{
    return *_mesh;
}

// 函数: get_mesh（常量）
const libMesh::Mesh &
MeshManager::get_mesh() const
{
    return *_mesh;
}

// 函数: get_subdomain_ids
// 作用: 收集本进程拥有的活动单元的子区域 id，并去重升序返回。
// 说明: 并行情况下仅返回本地集合，若需全局合集可在调用侧做 allgather 合并。
std::vector<libMesh::subdomain_id_type>
MeshManager::get_subdomain_ids() const
{
    std::set<libMesh::subdomain_id_type> id_set;
    for (auto it = _mesh->active_local_elements_begin(); it != _mesh->active_local_elements_end();
         ++it)
    {
        const libMesh::Elem * elem = *it;
        id_set.insert(elem->subdomain_id());
    }
    return std::vector<libMesh::subdomain_id_type>(id_set.begin(), id_set.end());
}

// 函数: get_boundary_ids
// 作用: 遍历本进程拥有的活动单元边界面，通过 BoundaryInfo 提取所有出现过的边界
// id，去重升序返回。 说明: 并行情况下仅返回本地集合，若需全局合集可在调用侧做
// allgather 合并。
std::vector<libMesh::boundary_id_type>
MeshManager::get_boundary_ids() const
{
    const libMesh::BoundaryInfo & boundary_info = _mesh->get_boundary_info();
    std::set<libMesh::boundary_id_type> id_set;

    for (auto it = _mesh->active_local_elements_begin(); it != _mesh->active_local_elements_end();
         ++it)
    {
        const libMesh::Elem * elem = *it;
        const unsigned int side_count = elem->n_sides();
        for (unsigned int s = 0; s < side_count; ++s)
        {
            if (!elem->neighbor_ptr(s))
            {
                std::vector<libMesh::boundary_id_type> side_ids;
                boundary_info.boundary_ids(elem, s, side_ids);
                for (auto id : side_ids)
                    id_set.insert(id);
            }
        }
    }

    return std::vector<libMesh::boundary_id_type>(id_set.begin(), id_set.end());
}

} // namespace cem